/**
 * 
 * author: liuxu
 * date: 2014-11-05
 *
 * provide an easy way to insert entries of files or folders into
 * media database. based on MediaScannerConnection.
 * 
 * Demo: com.lx.utils.demo.ActivityFilePickerDemo.startDlg3()
 */

package lx.utils.system;

import java.io.File;
import java.util.ArrayList;

import android.content.Context;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;

public class MediaScannerUtils {

    /**
     * callback when scan is done.
     */
    public interface ScannerListener {

        /**
         * callback when scan is done.
         * when scanning a folder, this will be invoked when all
         * files and folders under the given folder is scanned.
         * Note: this method will be invoked in main thread (UI thread).
         * @param path
         *            the file or folder being scanned
         * @param success
         *            whether the scan operation is success. when scanning a
         *            folder, success will be set to true only when all files
         *            and folders under the given folder is successfully
         *            scanned.
         */
        public void onScanCompleted(String path, boolean success);
    }

    /**
     * scan a file or folder
     * @param context
     * @param file
     *            path the file or folder to be scanned
     */
    public static void scanFile(Context context, String file) {
        scanFile(context, file, null);
    }

    /**
     * scan a file or folder. ScannerListener.onScanCompleted() will
     * be invoked in main thread (UI thread) when scan is done.
     * see ScannerListener for details.
     * @param context
     * @param file
     *            path the file or folder to be scanned
     * @param listener
     *            callback when scan is done
     */
    public static void scanFile(
            Context context, String file, ScannerListener listener) {
        MediaScannerTool mst = new MediaScannerTool(context, file, listener);
        mst.scan();
    }

    private static class MediaScannerTool {

        private int mScanCount = 0;
        private boolean mSuccess = true;
        private Context mContext;
        private File mScanPath;
        private ArrayList<File> mScanList;
        private MediaScannerConnection mConnection;
        private ScannerListener mListener;

        private MediaScannerConnectionClient mClient =
                new MediaScannerConnectionClient() {

                    @Override
                    public void onMediaScannerConnected() {
                        doScan();
                    }

                    @Override
                    public void onScanCompleted(String path, Uri uri) {
                        mSuccess = mSuccess && (uri != null);
                        mScanCount++;
                        if (mScanCount == mScanList.size()) {
                            // all files has been scanned.
                            // Note:
                            // MediaScannerConnectionClient.onScanCompleted()
                            // will be invoked in media thread by default. so
                            // here we callback to the UI thread.
                            callbackToUiThreadOnComplete();
                        }
                    }
                };

        MediaScannerTool(Context context, String path, ScannerListener listener) {
            mContext = context;
            mScanPath = new File(path);
            mListener = listener;
        }

        public void scan() {
            if (!mScanPath.exists()) {
                if (mListener != null) {
                    mListener.onScanCompleted(mScanPath.getAbsolutePath(), false);
                }
                return;
            }
            prepare();
            mConnection = new MediaScannerConnection(mContext, mClient);
            mConnection.connect();
        }

        private void prepare() {
            mScanCount = 0;
            mScanList = new ArrayList<File>();
            prepareScanList(mScanPath);
        }

        // put all files and folders that need to be scanned into a list.
        // we do this before scan really happens so that the file count
        // is known beforehand, thus we can know whether all files has
        // been scanned in MediaScannerConnectionClient.onScanCompleted()
        private void prepareScanList(File path) {
            mScanList.add(path);
            if (path.isDirectory()) {
                File[] subPaths = path.listFiles();
                for (File p : subPaths) {
                    prepareScanList(p);
                }
            }
        }

        private void doScan() {
            for (File f : mScanList) {
                mConnection.scanFile(f.getAbsolutePath(), null);
            }
        }

        private void callbackToUiThreadOnComplete() {
            if (mListener == null) {
                return;
            }
            Handler uiHandler = new Handler(Looper.getMainLooper());
            uiHandler.post(new Runnable() {
                @Override
                public void run() {
                    mListener.onScanCompleted(
                            mScanPath.getAbsolutePath(), mSuccess);
                }
            });
        }
    }

}
